/*********************************************************************************
 *      Copyright:  (C) 2019 Wu Yujun<540726307@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  mosq_pub.c
 *    Description:  This file mosquitto publish Demo
 *                 
 *        Version:  1.0.0(2019年01月28日)
 *         Author:  Wu Yujun <540726307@qq.com>
 *      ChangeLog:  1, Release initial version on "2019年01月28日 16时05分17秒"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <libgen.h>
#include <getopt.h>

#include "mosquitto.h"
#include "cJSON.h"
#include "mosq_conf.h"
#include "iniparser.h"


#define MSG_MAX_SIZE    512
#define INI_PATH "./mqtt_ali_conf.ini"



void printJson(cJSON * root) ;//以递归的方式打印json的最内层键值对
void message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message) ;
void pub_conn_callback(struct mosquitto *mosq, void *obj, int rc) ;


typedef struct _st_pub_ctx
{
    char    identifier[64] ;    //value identifier in jSON
    char    topic[256] ;        
    int     pubQos ;
    char    method[256] ; 
    char    json_id[256] ;
    char    version[32] ;

}st_pub_ctx ;


void printJson(cJSON * root)//以递归的方式打印json的最内层键值对
{
    int             i ;
    for(i=0; i<cJSON_GetArraySize(root); i++)   //遍历最外层json键值对
    {
        cJSON * item = cJSON_GetArrayItem(root, i);        
        if(cJSON_Object == item->type)      //如果对应键的值仍为cJSON_Object就递归调用printJson
            printJson(item);
        else                                //值不为json对象就直接打印出键和值
        {
            printf("%s->", item->string);
            printf("%s\n", cJSON_Print(item));
        }
    }
}

void pub_conn_callback(struct mosquitto *mosq, void *obj, int rc)
{
    int             mid ;
    st_pub_ctx      *mosq_ctx ;
    char            *msg; 
    cJSON           * root =  cJSON_CreateObject();
    cJSON           * item =  cJSON_CreateObject();
    int             retain = 0;
    char            *payload = "mosquitto Demo Test aliyun" ;

    if(!obj)
    {
        printf("Invail parameter in %s\n", __FUNCTION__) ;
        return ;
    }
    mosq_ctx = (st_pub_ctx *)obj ;

    cJSON_AddItemToObject(root, "method", cJSON_CreateString(mosq_ctx->method));//根节点下添加
    cJSON_AddItemToObject(root, "id", cJSON_CreateString(mosq_ctx->json_id));
    cJSON_AddItemToObject(root, "params", item);
    cJSON_AddItemToObject(item, mosq_ctx->identifier, cJSON_CreateString(payload));
    cJSON_AddItemToObject(root, "version", cJSON_CreateString(mosq_ctx->version));

    msg = cJSON_Print(root) ;
    printf("%s\n", msg);

    if(!rc)
    {
        if( mosquitto_publish(mosq,&mid,mosq_ctx->topic,strlen(msg)+1, msg, mosq_ctx->pubQos, retain) != MOSQ_ERR_SUCCESS )  
        {   
            printf("Mosq_Publish() error: %s\n", strerror(errno));
            return ;
        }
        printf("pubilush topic:%s\n",mosq_ctx->topic) ;
    }
    mosquitto_disconnect(mosq) ;
}


void print_usage(const char *program_name)
{
    printf("%s -- (2019.7.23)\n", program_name);
    printf(" Usage:%s [-h <host_name>] [-p <server_port>] [-i<client_id>] [-u <user>] [-P <passwd>] [-t <topic>] [-H <use_help>]\n", program_name);
    printf("        -p --port       the port of the server you want to connect\n") ;
    printf("        -h --host       the hostname of the server you want to connect\n") ;
    printf("        -u --user       the username of the client\n") ;
    printf("        -P --passwd     the passwd of the client you\n") ;
    printf("        -t --topic      the topic of the client you want to sub\n") ;
    printf("        -H --help       the client file how to use\n");
    printf("        -d --daemon     the client progame running in backgruand\n");

    return ;
}


int main(int argc, char **argv)
{
    int                 rv = -1 ;
    st_mqtt             mqtt ;
    struct mosquitto    *mosq = NULL;
    bool                session = true ;
    st_pub_ctx         mosq_ctx ;


    int                 opt = -1 ;
    int                 daemon_run = 0 ;
    char                *program_name ;
    char                *host=NULL ;
    int                 port = 0 ;
    char                *id = NULL ;
    char                *user = NULL ;
    char                *passwd = NULL ;
    char                *topic = NULL ;

    program_name = basename(argv[0]) ;

    const char *short_opts = "i:h:p:P:u:t:dH";                    
    const struct option long_opts[] =   {  
        {"host", required_argument, NULL, 'h'},    
        {"daemon", no_argument, NULL, 'd'},  
        {"help", no_argument, NULL, 'H'},  
        {"port", required_argument, NULL, 'p'},  
        {"id", required_argument, NULL, 'i'},  
        {"passwd", required_argument, NULL, 'P'},  
        {"user", required_argument, NULL, 'u'},  
        {"topic", required_argument, NULL, 't'},  
        {0, 0, 0, 0} 
    };  
    while ((opt= getopt_long(argc, argv, short_opts, long_opts,NULL)) != -1) 
    {
        switch (opt) 
        {
            case 'i':
                id = optarg ;
                break ;
            case 'P':
                passwd = optarg ;
                break ;
            case 'u':
                user = optarg ;
                break ;
            case 'p':
                port = atoi(optarg) ;
                break ;
            case 'd':
                daemon_run = 1 ;
                break ;
            case 'h':
                host = optarg ;
                break ;
            case 't':
                topic  = optarg ;
                break ;
            case 'H':
                print_usage(program_name) ;
                return 0 ;
        }
    }
    set_mqtt_conf(INI_PATH,host, port, id, user, passwd, topic) ;

    memset(&mosq_ctx,0,sizeof(mosq_ctx)) ;
    memset(&mqtt,0,sizeof(mqtt)) ;

    rv = gain_mqtt_conf(INI_PATH,&mqtt,TYPE_PUB) ;
    if(rv < 0)
    {
        printf("gain mqtt conf error\n") ;
        return -1 ;
    }
    strncpy( mosq_ctx.topic, mqtt.topic, sizeof(mosq_ctx.topic)) ;
    strncpy( mosq_ctx.identifier, mqtt.identifier, sizeof(mosq_ctx.identifier)) ;

    /*   Program running in backgrund    */
    if(daemon_run)
    {
        daemon(1,1) ;
    }
    
    memset(&mosq_ctx,0,sizeof(mosq_ctx)) ;
    memset(&mqtt,0,sizeof(mqtt)) ;

    rv = gain_mqtt_conf(INI_PATH,&mqtt,TYPE_PUB) ;
    if(rv < 0)
    {
        printf("gain mqtt conf error\n") ;
        return -1 ;
    }
    strncpy( mosq_ctx.topic, mqtt.topic, sizeof(mosq_ctx.topic)) ;
    
    strncpy( mosq_ctx.identifier, mqtt.identifier, sizeof(mosq_ctx.identifier)) ;
    strncpy( mosq_ctx.method, mqtt.method, sizeof(mosq_ctx.method)) ;
    strncpy( mosq_ctx.json_id, mqtt.json_id, sizeof(mosq_ctx.json_id)) ;
    strncpy( mosq_ctx.version, mqtt.version, sizeof(mosq_ctx.version)) ;
    mosq_ctx.pubQos = 0 ;

    /*  必须在任何其他mosquitto功能之前调用。 */
    mosquitto_lib_init() ;

    /*  创建一个新的mosquitto客户端实例 */
    mosq = mosquitto_new(mqtt.clientid, session, (void*)&mosq_ctx) ;
    if(!mosq)
    {
        printf("Mosquitto_new() failed: %s\n", strerror(errno)) ;
        goto cleanup ;
    }
    printf("Create mosquitto sucessfully!\n");

    /*     Set callback function    */
    mosquitto_connect_callback_set(mosq, pub_conn_callback) ;


    /*  配置mosquitton实例的用户名和密码    */
    if( mosquitto_username_pw_set(mosq, mqtt.username, mqtt.passwd) != MOSQ_ERR_SUCCESS ) 
    {
        printf("Mosq_usee_pw_set() failed: %s\n", strerror(errno) );
        goto cleanup ;
    }

    while(1)
    {
        /*   连接到MQTT代理。  */
        if( mosquitto_connect(mosq, mqtt.hostname, mqtt.port, KEEP_ALIVE) )
        {
            printf("connect mqtt server failed\n") ;
            continue ;
            sleep(1) ;
        }
        mosquitto_loop_forever(mosq, -1, 1);
        sleep(10) ;
    }


cleanup:
    printf("program will exit\n");
    mosquitto_destroy(mosq) ;
    mosquitto_lib_cleanup() ;

    return 0 ;
}
